﻿<template>
  <div class="code-workspace">
    <div class="editor-header">
      <div class="language-selector">
        <span class="label">编程语言：</span>
        <el-select v-model="language" size="small" class="lang-select" :disabled="readonly">
          <el-option v-for="lang in languages" :key="lang.value" :label="lang.label" :value="lang.value" />
        </el-select>
      </div>
      <div class="limits-info">
        <span class="limit-item" v-if="timeLimit">
          <el-icon><Timer /></el-icon>
          时间限制：{{ timeLimit }}ms
        </span>
        <span class="limit-item" v-if="memoryLimit">
          <el-icon><DataAnalysis /></el-icon>
          内存限制：{{ memoryLimit }}MB
        </span>
      </div>
      <div v-show="!readonly" class="theme-toggle">
        <el-switch v-model="isDark" active-text="暗色" inactive-text="亮色" inline-prompt />
      </div>
    </div>

    <div class="editor-container">
      <Codemirror ref="cmRef" v-model:value="code" :options="editorOptions" />
    </div>

    <div v-if="!readonly" class="editor-actions">
      <!-- 解决方案按钮插槽（左侧） -->
      <div class="left-buttons">
        <slot name="solution-actions"></slot>
      </div>

      <!-- 操作按钮（右侧） -->
      <div class="right-buttons">
        <el-button type="primary" :loading="isProcessing && !isSubmission" @click="handleRunCode">
          <el-icon><VideoPlay /></el-icon>
          {{ isProcessing && !isSubmission ? "运行中..." : "运行代码" }}
        </el-button>
        <el-button type="success" :loading="isProcessing && isSubmission" @click="handleSubmitCode">
          <el-icon><Upload /></el-icon>
          {{ isProcessing && isSubmission ? "提交中..." : "提交代码" }}
        </el-button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue"
import Codemirror from "codemirror-editor-vue3"
import { DataAnalysis, Timer, Upload, VideoPlay } from "@element-plus/icons-vue"
import { ElMessage } from "element-plus"
import { submitCode } from "@/api/modules/problem"
// CodeMirror 依赖
import "codemirror/mode/javascript/javascript.js"
import "codemirror/mode/python/python.js"
import "codemirror/mode/clike/clike.js"
import "codemirror/theme/material-darker.css"
import "codemirror/addon/edit/matchbrackets.js"
import "codemirror/addon/edit/closebrackets.js"
import "codemirror/addon/selection/active-line.js"
import "codemirror/addon/hint/show-hint.js"
import "codemirror/addon/hint/javascript-hint.js"
import "codemirror/addon/hint/show-hint.css"

// 当前支持的判题语言
const SUPPORTED_JUDGE_LANGUAGES = ["python", "java"]
const LANGUAGE_SUPPORT_MESSAGE = "系统正在测试中，目前仅支持 Python 和 Java 语言"

// 组件属性
const props = defineProps({
  problemId: { type: Number, required: true },
  initialCode: { type: String, default: "" },
  initialLanguage: { type: String, default: "python" },
  signatureMap: {
    type: Object,
    default: () => ({})
  },
  readonly: { type: Boolean, default: false },
  timeLimit: { type: Number, default: 0 },
  memoryLimit: { type: Number, default: 0 }
})

const emit = defineEmits(["update-result", "language-change"])

// 代码编辑器状态
const code = ref(props.initialCode || "")
const language = ref(props.initialLanguage || "python")
const isDark = ref(false)
const cmRef = ref()
const isProcessing = ref(false)
const isSubmission = ref(false)

// 语言选项
const languages = [
  { label: "Python", value: "python" },
  { label: "JavaScript", value: "javascript" },
  { label: "Java", value: "java" },
  { label: "C++", value: "cpp" }
]

// 编辑器选项
const editorOptions = computed(() => ({
  // 设置编程语言的语法高亮模式
  mode: (() => {
    const modeMap = {
      python: "text/x-python",
      javascript: "text/javascript",
      java: "text/x-java",
      cpp: "text/x-c++src"
    }
    return modeMap[language.value] || "text/plain"
  })(),
  theme: isDark.value ? "material-darker" : "default", // 主题设置，根据isDark切换
  lineNumbers: true, // 显示行号
  tabSize: 4, // Tab键缩进宽度为4个空格
  indentWithTabs: false, // 使用空格而非Tab字符进行缩进
  smartIndent: true, // 智能缩进
  matchBrackets: true, // 高亮匹配的括号
  autoCloseBrackets: true, // 自动闭合括号
  styleActiveLine: true, // 高亮当前行
  extraKeys: { Ctrl: "autocomplete" }, // 快捷键设置：Tab触发代码补全
  readOnly: props.readonly // 是否为只读模式
}))

// 处理代码变化
const handleCodeChange = () => {
  // 可以在这里添加代码变化的处理逻辑
}

// 设置代码模板
const setCodeTemplate = (lang, signature) => {
  if (!signature) {
    // 如果数据库没有提供函数签名，则使用默认模板
    if (lang === "python") {
      code.value = `def solution(input_data):\n    # 请在此处编写代码\n    return`
    } else if (lang === "javascript") {
      code.value = `function solution(input) {\n    // 请在此处编写代码\n    return;\n}`
    } else if (lang === "java") {
      code.value = `public class Solution {\n    public static String main(String str) {\n        // 请在此处编写代码\n        return null;\n    }\n}`
    } else if (lang === "cpp") {
      code.value = `#include <iostream>\n\nint solution(int input) {\n    // 请在此处编写代码\n    return 0;\n}`
    }
    return
  }

  // 当有函数签名时，直接使用函数签名作为模板
  if (lang === "python") {
    // 检查签名是否已经包含函数体
    if (signature.includes(":")) {
      code.value = `${signature}\n    # 请在此处编写代码\n    return`
    } else {
      code.value = `${signature}:\n    # 请在此处编写代码\n    return`
    }
  } else if (lang === "javascript") {
    // 检查签名是否已经包含花括号
    if (signature.trim().endsWith("{")) {
      code.value = `${signature}\n    // 请在此处编写代码\n    return;\n}`
    } else {
      code.value = `${signature} {\n    // 请在此处编写代码\n    return;\n}`
    }
  } else if (lang === "java") {
    // 检查签名是否已经包含花括号
    if (signature.trim().endsWith("{")) {
      code.value = `public class Solution {\n    ${signature}\n        // 请在此处编写代码\n        return null;\n    }\n}`
    } else {
      code.value = `public class Solution {\n    ${signature} {\n        // 请在此处编写代码\n        return null;\n    }\n}`
    }
  } else if (lang === "cpp") {
    // 检查签名是否已经包含花括号
    if (signature.trim().endsWith("{")) {
      code.value = `${signature}\n    // 请在此处编写代码\n    return 0;\n}`
    } else {
      code.value = `${signature} {\n    // 请在此处编写代码\n    return 0;\n}`
    }
  }
}

// 刷新编辑器
const refresh = () => {
  nextTick(() => {
    const editor = cmRef.value?.cminstance
    if (editor) {
      editor.refresh()
      editor.focus()
    }
  })
}

// 运行代码
const runCode = async (isSubmit = false) => {
  if (!code.value.trim()) {
    ElMessage.warning("请先编写代码")
    return
  }

  if (isProcessing.value) return

  isProcessing.value = true
  isSubmission.value = isSubmit

  try {
    // 调用后端API进行判题
    const requestData = {
      code: code.value,
      language: language.value,
      isSubmission: isSubmit
    }

    const response = await submitCode(props.problemId, requestData)

    // 后端返回ApiResponse格式，http拦截器已包装为 { code, data, message }
    const judgeResult = response?.data ?? response

    const result = {
      status: judgeResult.status?.code || judgeResult.status, // 处理枚举类型
      message: judgeResult.status?.code === "AC" ? "恭喜！所有测试用例通过" : "部分测试用例未通过",
      score: judgeResult.score,
      total_score: judgeResult.totalScore,
      time_usage: Math.round((judgeResult.executionTime || 0) * 1000), // 转换为毫秒
      memory_usage: judgeResult.memoryUsage,
      test_cases_passed: judgeResult.passedCases,
      total_test_cases: judgeResult.totalCases,
      isSubmission: isSubmit,
      error_message: judgeResult.errorMessage,
      detail_results: {
        cases:
          judgeResult.testResults?.map(testCase => ({
            input: testCase.input,
            output: testCase.expected,
            actual: testCase.actual,
            status: testCase.status?.code || testCase.status,
            error: testCase.error
          })) || []
      }
    }

    emit("update-result", result)

    if (judgeResult.status?.code === "AC" || judgeResult.status === "AC") {
      ElMessage.success(isSubmit ? "提交成功！所有测试用例通过" : "运行成功！")
    } else {
      ElMessage.warning(isSubmit ? "提交完成，但部分测试用例未通过" : "运行完成，请查看结果")
    }
  } catch (error) {
    // 根据错误类型显示不同的错误信息
    let errorMessage = "执行失败"
    if (error.response?.data?.message) {
      errorMessage = error.response.data.message
    } else if (error.message) {
      errorMessage = error.message
    }

    ElMessage.error(`执行失败: ${errorMessage}`)

    // 发送错误结果
    emit("update-result", {
      status: "ERROR",
      message: errorMessage,
      error_message: errorMessage,
      isSubmission: isSubmit
    })
  } finally {
    isProcessing.value = false
  }
}

// 检查语言是否支持
const checkLanguageSupport = () => {
  if (!SUPPORTED_JUDGE_LANGUAGES.includes(language.value)) {
    ElMessage.info(LANGUAGE_SUPPORT_MESSAGE)
    return false
  }
  return true
}

// 运行和提交代码
const handleRunCode = async () => {
  if (!checkLanguageSupport()) return
  await runCode(false)
}

const handleSubmitCode = async () => {
  if (!checkLanguageSupport()) return
  await runCode(true)
}

// 监听主题变化
watch(isDark, newVal => {
  const editor = cmRef.value?.cminstance
  if (editor) {
    editor.setOption("theme", newVal ? "dark" : "default")
  }
})

// 监听语言变化
watch(language, (newLang, oldLang) => {
  if (newLang !== oldLang) {
    const signature = props.signatureMap[newLang] || ""
    setCodeTemplate(newLang, signature)
    refresh()
    emit("language-change", newLang)
  }
})

// 组件初始化
onMounted(() => {
  // 设置初始语言
  language.value = props.initialLanguage

  // 设置初始代码
  if (props.initialCode && props.initialCode.trim() !== "") {
    code.value = props.initialCode
  } else {
    const signature = props.signatureMap[language.value] || ""
    setCodeTemplate(language.value, signature)
  }

  // 初始化编辑器
  setTimeout(refresh, 100)
})

// 监听属性变化
watch(
  () => props.initialCode,
  newVal => {
    if (newVal && newVal !== code.value) {
      code.value = newVal
      setTimeout(refresh, 100)
    }
  }
)

watch(
  () => props.initialLanguage,
  newVal => {
    if (newVal && newVal !== language.value) {
      language.value = newVal
    }
  }
)

// 组件销毁时，清理资源
onBeforeUnmount(() => {
  // 清理编辑器实例
  const editor = cmRef.value?.cminstance
  if (editor) {
    try {
      cmRef.value = undefined
    } catch (e) {
      // 清理失败
    }
  }
})

defineExpose({
  code,
  language,
  refresh
})
</script>

<style scoped>
.code-workspace {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.editor-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  background-color: var(--editor-header-bg);
  border-bottom: 1px solid var(--editor-border);
  height: 44px;
  box-sizing: border-box;
}

.language-selector {
  display: flex;
  align-items: center;
  gap: 10px;
}

.language-selector .label {
  font-size: 14px;
  color: var(--color-text-primary);
  font-weight: 500;
  white-space: nowrap;
}

.lang-select {
  width: 150px;
}

.limits-info {
  display: flex;
  align-items: center;
  gap: 16px;
  color: var(--color-text-secondary);
  font-size: 13px;
}

.limit-item {
  display: flex;
  align-items: center;
  gap: 4px;
}

.limit-item {
  font-size: 14px;
  color: var(--color-text-primary);
  font-weight: 500;
}

.theme-toggle {
  display: flex;
  align-items: center;
}

.editor-container {
  flex: 1;
  overflow: hidden;
  background-color: transparent;
  display: flex;
  min-height: 400px;
}

:deep(.CodeMirror) {
  height: 100% !important;
  width: 100% !important;
  font-family: "Source Code Pro", "Menlo", "Monaco", "Consolas", "Courier New", monospace;
  font-size: 14px;
  line-height: 1.5;
  border: none !important;
  letter-spacing: 0.2px;
  background-color: var(--editor-bg) !important;
  color: var(--editor-text) !important;
}

:deep(.CodeMirror-gutters) {
  border-right: 1px solid var(--editor-border) !important;
  background-color: var(--editor-gutter-bg) !important;
}

:deep(.CodeMirror-linenumber) {
  color: var(--editor-line-number);
  padding: 0 8px;
  font-size: 13px;
}

:deep(.CodeMirror-activeline-background) {
  background-color: var(--editor-active-line) !important;
}

:deep(.cm-s-material-darker) {
  background-color: var(--editor-bg) !important;
  color: var(--editor-text) !important;
}

:deep(.cm-s-material-darker .CodeMirror-gutters) {
  background-color: var(--editor-gutter-bg) !important;
  border-right-color: var(--editor-border) !important;
}

:deep(.cm-s-material-darker .CodeMirror-linenumber) {
  color: var(--editor-line-number) !important;
}

:deep(.cm-s-material-darker .CodeMirror-activeline-background) {
  background-color: var(--editor-active-line) !important;
}

.editor-actions {
  display: flex;
  justify-content: space-between;
  padding: 9px 16px;
  background-color: transparent;
  border-top: 1px solid var(--editor-border);
  height: 50px;
  box-sizing: border-box;
}

.left-buttons {
  display: flex;
  gap: 12px;
  align-items: center;
}

.right-buttons {
  display: flex;
  gap: 12px;
  align-items: center;
}
</style>
